package com.lglbc.day18.config;


import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.lglbc.day18.entity.AccessToken;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jwk.RSAKey;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.UUID;

@Service
public class JwtTokenUtil {

    public static String generateTokenByHMAC(String payloadStr, String secret) throws JOSEException {
        //创建JWS头，设置签名算法和类型
        JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.HS256)
                .type(JOSEObjectType.JWT).build();
        //将负载信息封装到Payload中
        Payload payload = new Payload(payloadStr);
        //创建JWS对象
        JWSObject jwsObject = new JWSObject(jwsHeader, payload);
        //创建HMAC签名器
        JWSSigner jwsSigner = new MACSigner(secret);
        //签名
        jwsObject.sign(jwsSigner);
        return jwsObject.serialize();


    }

    public static AccessToken verifyTokenByHMAC(String token, String secret) throws ParseException, JOSEException {
        //从token中解析JWS对象
        JWSObject jwsObject = JWSObject.parse(token);
        //创建HMAC验证器
        JWSVerifier jwsVerifier = new MACVerifier(secret);
        if (!jwsObject.verify(jwsVerifier)) {
            throw new RuntimeException("token签名不合法");
        }
        String payload = jwsObject.getPayload().toString();
        System.out.println(payload);
        AccessToken payloadDto = JSONUtil.toBean(payload, AccessToken.class);
        if (payloadDto.getExp() < new Date().getTime()) {
            throw new RuntimeException("token已过期！");
        }
        return payloadDto;
    }

    public static String generateTokenByRSA(String userName, List<String> authorities) {
        try {
            //创建JWS头，设置签名算法和类型
            JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.RS256)
                    .type(JOSEObjectType.JWT)
                    .build();
            String accessToken = createAccessToken(userName, authorities);
            //将负载信息封装到payload中
            Payload payload = new Payload(accessToken);
            //创建JWS对象
            JWSObject jwsObject = new JWSObject(jwsHeader, payload);
            //创建RSA签名器
            JWSSigner jwsSigner = new RSASSASigner(getDefaultRSAKey(), true);
            //签名
            jwsObject.sign(jwsSigner);
            return jwsObject.serialize();
        } catch (JOSEException e) {
            return null;
        }
    }

    public static AccessToken verifyTokenByRSA(String token) {
        //从token中解析JWS对象
        JWSObject jwsObject = null;
        try {
            jwsObject = JWSObject.parse(token);
            RSAKey publicRsaKey = getDefaultRSAKey().toPublicJWK();
            //使用RSA公钥创建RSA验证器
            JWSVerifier jwsVerifier = new RSASSAVerifier(publicRsaKey);
            if (!jwsObject.verify(jwsVerifier)) {
                return null;
            }
            String payload = jwsObject.getPayload().toString();
            AccessToken payloadDto = JSONUtil.toBean(payload, AccessToken.class);
            if (payloadDto.getExp() < new Date().getTime()) {
                return null;
            }
            return payloadDto;
        } catch (Exception e) {
            return null;
        }
    }

    private static String createAccessToken(String userName, List<String> authorities) {
        Date now = new Date();
        Date exp = DateUtil.offsetSecond(now, 60 * 60);
        AccessToken token = AccessToken.builder()
                .sub("macro")
                .iat(now.getTime())
                .exp(exp.getTime())
                .jti(UUID.randomUUID().toString())
                .username(userName)
                .authorities(authorities)
                .build();
        return JSONUtil.toJsonStr(token);

    }


    public static RSAKey getDefaultRSAKey() {
        try {
            KeyStore keyStore = KeyStore.getInstance("JKS");
            InputStream inputStream = new ClassPathResource("jwt.jks").getInputStream();
            keyStore.load(inputStream, "123321".toCharArray());

            PrivateKey privateKey = (PrivateKey) keyStore.getKey("jwt", "123321".toCharArray());

            RSAPublicKey publicKey = (RSAPublicKey) keyStore.getCertificate("jwt").getPublicKey();

            return new RSAKey.Builder(publicKey).privateKey(privateKey).build();
        } catch (Exception e) {
            return null;
        }

    }
}

